package com.tianji.promotion.service.impl;

import com.tianji.api.dto.trade.CouponDiscountDTO;
import com.tianji.api.dto.trade.OrderCourseDTO;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.PermuteUtil;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.dto.DiscountType;
import com.tianji.promotion.entity.Coupon;
import com.tianji.promotion.entity.CouponScope;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.tianji.promotion.service.IDiscountService;
import com.tianji.promotion.strategy.discount.DiscountStrategy;
import com.tianji.promotion.strategy.discount.DiscountStrategyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DiscountServiceImpl implements IDiscountService {


    @Autowired
    private ICouponService couponService;
    @Autowired
    private ICouponScopeService scopeService;


    @Override
    public List<CouponDiscountDTO> findDiscountSolution(List<OrderCourseDTO> orderCourses) {
        Long userId = UserContext.getUser();
        // 查询当前用户所有可用的优惠券，user_coupon  coupon
        List<Coupon> coupons = couponService.getAvailableByUserId(userId);
        if(CollUtils.isEmpty(coupons)){
            return new ArrayList<>();
        }
        // 初步筛选，判断优惠券是否满足使用的门槛，先不关心券的适用范围，把不满足门槛的先筛掉
        // 计算订单的总价
        int orderTotalAmount = orderCourses.stream().mapToInt(OrderCourseDTO::getPrice).sum();
        List<Coupon> availableCoupons = new ArrayList<>();
        for (Coupon coupon : coupons) {
            DiscountType discountType = DiscountType.of(coupon.getDiscountType());
            DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscount(discountType);
            boolean canUse = discountStrategy.canUse(orderTotalAmount, coupon);
            if(canUse){
                availableCoupons.add(coupon);
            }
        }
        // 判断是否为空
        if(CollUtils.isEmpty(availableCoupons)){
            return new ArrayList<>();
        }
        // 记录优惠券id和能够应用到的订单商品列表的映射关系
        Map<Long, List<OrderCourseDTO>> couponCourseMap = new HashMap<>();
        // 细筛，根据适用范围筛选
        List<Coupon> remainCoupons = new ArrayList<>();
        for (Coupon coupon : availableCoupons) {
            Boolean specific = coupon.getSpecific();
            // 没有指定范围，相当于对订单中所有的商品都适用，只需要满足门槛就可以
            if(specific == null || !specific){
                remainCoupons.add(coupon);
                couponCourseMap.put(coupon.getId(), orderCourses);
            }else{
                // 带范围
                List<CouponScope> scopes = scopeService.getByCouponId(coupon.getId());
                AssertUtils.isNotNull(scopes, "范围不能为空");
                // 找到订单中指定范围scopes的商品的价格之和，判断是否满足券的门槛
                // 保存券可以应用到的课程的总价
                List<OrderCourseDTO> couponCourseList = new ArrayList<>();
                // 遍历订单中的每一个商品
                for (OrderCourseDTO orderCourse : orderCourses) {
                    Long orderCategoryId = orderCourse.getCateId();
                    // 判断商品是否在券的应用范围之内
                    for (CouponScope scope : scopes) {
                        Long scopeCategoryId = scope.getBizId();
                        if(Objects.equals(orderCategoryId, scopeCategoryId)){
                            couponCourseList.add(orderCourse);
                            break;
                        }
                    }
                }
                if(CollUtils.isEmpty(couponCourseList)){
                    continue;
                }
                couponCourseMap.put(coupon.getId(), couponCourseList);
                // 判断商品的价格是否满足门槛
                DiscountType discountType = DiscountType.of(coupon.getDiscountType());
                DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscount(discountType);
                int couponCourseAmount = couponCourseList.stream().mapToInt(OrderCourseDTO::getPrice).sum();
                boolean canUse = discountStrategy.canUse(couponCourseAmount, coupon);
                if(canUse){
                    remainCoupons.add(coupon);
                }
            }
        }
        // 判断是否为空
        if(CollUtils.isEmpty(remainCoupons)){
            return new ArrayList<>();
        }
        // 全排列
        List<List<Coupon>> solutions = PermuteUtil.permute(remainCoupons);
        // 添加单个券
        for (Coupon coupon : remainCoupons) {
            solutions.add(Arrays.asList(coupon));
        }
        // 计算优惠明细和实付金额
        // 一个solution有多个券，对应一个返回的CouponDiscountDTO
        List<CouponDiscountDTO> resultList = new ArrayList<>();
        for (List<Coupon> solution : solutions) {
            CouponDiscountDTO discountDTO = new CouponDiscountDTO();
            // 保存商品和优惠明细的对应关系
            Map<Long, Integer> courseDiscountMap = new HashMap<>();
            for (Coupon coupon : solution) {
                DiscountType discountType = DiscountType.of(coupon.getDiscountType());
                DiscountStrategy discountStrategy = DiscountStrategyFactory.getDiscount(discountType);
                // 找到订单中 符合范围的商品
                List<OrderCourseDTO> orderCourseDTOS = couponCourseMap.get(coupon.getId());
                // 计算券可以应用到的商品的总的金额
                int couponCourseAmount = orderCourseDTOS.stream().mapToInt((dto)->{
                    //商品的原价
                    Integer price = dto.getPrice();
                    // 商品的优惠
                    Integer discount = courseDiscountMap.getOrDefault(dto.getId(), 0);
                    return price - discount;
                }).sum();
                // 判断券是否可用于订单金额
                boolean canUse = discountStrategy.canUse(couponCourseAmount, coupon);
                if(!canUse){
                    continue;
                }
                // 计算总的优惠金额
                int totalDiscountAmount = discountStrategy.calculateDiscount(couponCourseAmount, coupon);
                // 遍历每一个商品，把优惠分摊到每一个商品上
                for (OrderCourseDTO orderCourse : orderCourseDTOS) {
                    // 获取商品剩余的金额
                    Integer coursePrice = orderCourse.getPrice() - courseDiscountMap.getOrDefault(orderCourse.getId(), 0);
                    // 计算商品金额在总价中的占比
                    double percentage = coursePrice * 1.0 / couponCourseAmount;
                    // 计算商品的优惠金额
                    int courseDiscount = (int)(totalDiscountAmount * percentage);
                    courseDiscountMap.put(orderCourse.getId(), courseDiscount + courseDiscountMap.getOrDefault(orderCourse.getId(),0));
                }
                // 组装返回结果
                discountDTO.getIds().add(coupon.getId());
                discountDTO.getRules().add(discountStrategy.getRule(coupon));
                discountDTO.setDiscountAmount(discountDTO.getDiscountAmount() + totalDiscountAmount);
            }
            // 一个方案的多个coupon都已经计算完成
            resultList.add(discountDTO);
        }//所有的方案都计算完了
        // 筛选最优解
        resultList = findBestSolution(resultList);
        // 返回值
        return resultList;
    }

    private List<CouponDiscountDTO> findBestSolution(List<CouponDiscountDTO> list) {
        // 1.准备Map记录最优解
        // 券相同，找优惠金额最高的
        Map<String, CouponDiscountDTO> moreDiscountMap = new HashMap<>();
        // 优惠金额相同，找券最少的
        Map<Integer, CouponDiscountDTO> lessCouponMap = new HashMap<>();
        // 2.遍历，筛选最优解
        for (CouponDiscountDTO solution : list) {
            // 2.1.计算当前方案的id组合
            String ids = solution.getIds().stream()
                    .sorted(Long::compare).map(String::valueOf).collect(Collectors.joining(","));
            // 2.2.比较用券相同时，优惠金额是否最大
            CouponDiscountDTO best = moreDiscountMap.get(ids);
            if (best != null && best.getDiscountAmount() >= solution.getDiscountAmount()) {
                // 当前方案优惠金额少，跳过
                continue;
            }
            // 2.3.比较金额相同时，用券数量是否最少
            best = lessCouponMap.get(solution.getDiscountAmount());
            int size = solution.getIds().size();
            if (size > 1 && best != null && best.getIds().size() <= size) {
                // 当前方案用券更多，放弃
                continue;
            }
            // 2.4.更新最优解
            moreDiscountMap.put(ids, solution);
            lessCouponMap.put(solution.getDiscountAmount(), solution);
        }
        // 3.求交集
        Collection<CouponDiscountDTO> bestSolutions = CollUtils
                .intersection(moreDiscountMap.values(), lessCouponMap.values());
        // 4.排序，按优惠金额降序
        return bestSolutions.stream()
                .sorted(Comparator.comparingInt(CouponDiscountDTO::getDiscountAmount).reversed())
                .collect(Collectors.toList());
    }


}
